from circuit import *

class Module():
    def __init__(self,
                 name : str,
                 input_wires : dict[str, Wire | dict[int, Wire]],
                 output_wires : dict[str, Wire | dict[int, Wire]],
                 wires : set[Wire],
                 circuits : set[Circuit]) -> None:
        self._name : str = name
        self._input_wires : dict[str, Wire | dict[int, Wire]] = input_wires
        self._output_wires : dict[str, Wire | dict[int, Wire]] = output_wires
        self._wires : set[Wire] = wires
        self._circuits : set[Circuit] = circuits

    def name(self) -> str:
        return self._name

    def input_wires(self) -> dict[str, Wire | dict[int, Wire]]:
        return self._input_wires

    def output_wires(self) -> dict[str, Wire | dict[int, Wire]]:
        return self._output_wires

    def wires(self) -> set[Wire]:
        return self._wires
    
    def circuits(self) -> set[Circuit]:
        return self._circuits

    def evaluate(self, inputs : dict[str, bool | dict[int, bool]]):
        self.__init_wires_value()
        self.__init_circuits_status()
        self.__assign_inputs(inputs)

        self.__do_evaluate()

    def __init_wires_value(self):
        for w in self._wires:
            w.value = None

    def __init_circuits_status(self):
        for c in self._circuits:
            c.evaluated = False

    def __assign_inputs(self, inputs : dict[str, bool | dict[int, bool]]):
        for signal_name in inputs:
            input_value = inputs[signal_name]
            
            if isinstance(input_value, bool):
                self._input_wires[signal_name].value = input_value
            else:
                for index in input_value:
                    self._input_wires[signal_name][index].value = input_value[index]

    def __do_evaluate(self):
        # Add all input wires to queue
        # The wire in queue means the wire's input is sure, 
        # but the circuits come form this wire have not been evaluted 
        queue : list[Wire] = []
        for wire in self._input_wires.values():
            if isinstance(wire, Wire):
                queue.append(wire)
            else:
                for w in wire.values():
                    queue.append(w)

        while len(queue) > 0:
            wire = queue.pop(0)

            all_evaluated = True
            for circuit in wire.output_circuits:
                if circuit.evaluated == True:
                    continue

                if circuit.evaluate() == True:
                    # The circuit has been evaluted, so its output wires are sure. Add them to queue
                    for w in circuit.output_wires.values():
                        queue.append(w)
                else:
                    all_evaluated = False
            
            if all_evaluated == False:
                queue.append(wire)